moddbfandomcom-20200213-history
VBNET:DataSharingMultithreading
Introduction A big problem with multithreaded applications is data sharing. If you look at file manipulations, the I/O routines lock the target file to prevent other processes from modifying the data while the current process is modifying it. If it didn't there would be data corruption and such (one process overwrites what another just wrote, so that they conflict later). The same can happen in multithreaded applications. To prevent this, you lock your variables as well, using the SyncLock statement. As an explanation, MSDN says it best: Quote: Monitor objects are used to ensure that a block of code runs without being interrupted by code running on other threads. In other words, code in other threads cannot run until code in the synchronized code block has finished. An Example Suppose, for example, that you have a program that repeatedly and asynchronously reads data and displays the results. With operating systems that use preemptive multitasking, a running thread can be interrupted by the operating system to allow time for some other thread to run. Without synchronization, it is possible that you could get a partially updated view of the data if the object that represents the data is modified by another thread while the data is being displayed. Monitor objects guarantee that a section of code will run without being interrupted. Visual Basic .NET provides the SyncLock and End SyncLock statements to simplify access to monitor objects. SyncLock SyncLock puts an exclusive lock on the object that is passed to it, preventing other threads from accessing it. SyncLock must be passed a reference object, and a handy one is the System.Type of the class itself. Here is how it would look in our example: Public Class FillClass Private intValue As Int32 Private intReturn(99) As Int32 Private intMultiplier As Int32 Public WriteOnly Property Value() Set(ByVal Value As Int32) intValue = Value End Set End Property Public WriteOnly Property Multiplier() Set(ByVal Value As Int32) intMultiplier = Value End Set End Property Public ReadOnly Property Return() Get Return = intReturn End Get End Property Public ReadOnly Property ReturnIndex(ByVal Index As Int32) Get Return = intReturn(index) End Get End Property Public Function Fill() SyncLock GetType(FillClass) Dim i As Int32 Dim current As Int32 current = intValue For i = 0 To 100 intReturn(i) = current current *= intMultiplier Next End SyncLock End Function End Class For instance, if this code would be run without a SyncLock, and another thread modifies intMultiplier (by using the property Multiplier) while this thread was running, the results would be incorrect. SyncLock prevents other processes from accessing the data until the block ends. Thread.Join Now, for a more complex problem using Thread.Join as mentioned earlier. Join is a method that allows you to waith till a thread completes before proceeding (blocks the current thread). It can also be passed a number as an argument. It waits that number in milliseconds, then returns true if the thread is finished, or false if not. In this example, we are going to spawn two new threads, then make another to compute the results from the previous two. Dim oThread(2) As System.Threading.Thread Dim oFill(2) As New FillClass 'start the first thread, setting 2 as the value oThread(0) = New Thread(AddressOf oFill(0).Fill) oFill(0).Value = 2 oFill(0).Multiplier = 2 oThread(0).Start() 'start the second, using 3 as the value oThread(1) = New Thread(AddressOf oFill(1).Fill) oFill(1).Value = 3 oFill(1).Multiplier = 2 oThread(1).Start() 'wait until the first thread is done oThread(0).Join 'waith until the second thread is done oThread(1).Join oThread(2) = New Thread(AddressOf oFill(2).Fill) oFill(2).Value = oThread(0).ReturnIndex(4) - oThread(1).ReturnIndex oFill(2).Multiplier = 2 oThread(2).Start() oThread(2).Join Dim i As Int32, retVal() As Int32 retVal = oThread(2).Return For i = 0 To UBound(retVal) Listbox1.Items.Add(retVal(i)) Next Ok, here's what happened. First, we declare two an array of threads, and an array of classes. Next we set the first thread to the first class' function, then fill the value, and finally start it. We do the same for the next one. Then, we call the oThread(0).Join, which will block until the thread finishes its execution. We do the same for the next thread, ensuring they are both done. Now we create a new thread and set its value, which is based on the previous two results (which is why we had to wait for the threads to finish execution, so we could use their values). We run, block until its done, then display the results in a Listbox.